﻿using System;
using System.Collections.Generic;
using System.Data.Entity.Infrastructure;
using System.Linq;
using System.Text;
using App.BaseObject;
using Newtonsoft.Json;
using EntityFramework.DynamicFilters;
using Abp.Domain.Entities;

namespace App.EntityFramework
{
    public static class WebPlusDbContextExtensions
    {
        /// <summary>
        /// 根据Entity SQL进行查询
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="db"></param>
        /// <param name="where"></param>
        /// <param name="page"></param>
        /// <param name="rows"></param>
        /// <param name="order"></param>
        /// <param name="count"></param>
        /// <param name="filterRules"></param>
        /// <param name="includes"></param>
        /// <param name="sort"></param>
        /// <returns></returns>
        public static IQueryable<T> QueryByESql<T>(this WebPlusDbContext db, string where, int page, int rows, string sort,string order,
            out int count, string filterRules = null, string[] includes = null, string tenantFilter = "")
        {
            if (string.IsNullOrEmpty(sort) || string.IsNullOrEmpty(order)) throw new Exception("使用该方法必须指定sort和order参数");
            var query = QueryByESql<T>(db, where, "c."+sort + " " + order, filterRules, includes, tenantFilter);
            count = query.Count();
            if (page >= 0 && rows > 0) return query.Skip((page - 1)*rows).Take(rows);
            return query;
        }

        /// <summary>
        /// 根据Entity SQL进行查询
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="db"></param>
        /// <param name="where"></param>
        /// <param name="sortorder"></param>
        /// <param name="filterRules"></param>
        /// <param name="includes"></param>
        /// <returns></returns>
        public static IQueryable<T> QueryByESql<T>(this WebPlusDbContext db, string where, string sortorder = null, string filterRules = null, string[] includes = null,string tenantFilter="")
        {
            db.DisableAllFilters();

            var entity = typeof(T);
            if (entity.GetInterface("IMustHaveTenant") != null || entity.GetInterface("IMayHaveTenant") != null)
            {
                if (!string.IsNullOrEmpty(where))
                {
                    where += " and ";
                }
                where += tenantFilter;
            }
            
            if (string.IsNullOrEmpty(filterRules))
            {
                filterRules = "[]";
            }
            var filters = JsonConvert.DeserializeObject<List<DataFilter>>(filterRules);
            var esql = new StringBuilder("select value c from [" + typeof(T).Name + "] as c");
            if (!string.IsNullOrEmpty(where) || filters.Count>0 )
            {
                esql.AppendLine(" where ");
            }
            if (!string.IsNullOrEmpty(where))
            {
                esql.AppendLine(where);
            }
            if (!string.IsNullOrEmpty(where) && filters.Count > 0)
            {
                esql.Append(" and ");
            }
            if (filters.Count > 0)
            {
                esql.AppendLine(GetEqlstrByEasyuiFilter(filters,
                    "c."));
            }
            if (!string.IsNullOrEmpty(sortorder))
            {
                esql.AppendLine(" order by " + sortorder);
            }

            var query = ((IObjectContextAdapter)db).ObjectContext.CreateQuery<T>(esql.ToString());
            if (includes != null) query = includes.Aggregate(query, (current, include) => current.Include(include));
            return query;
        }

        #region helpers
        /// <summary>
        /// 处理Easyui字段过滤
        /// </summary>
        /// <param name="filters"></param>
        /// <param name="a"></param>
        /// <returns></returns>
        public static string GetEqlstrByEasyuiFilter(List<DataFilter> filters, string a)
        {
            if (filters == null || filters.Count == 0)
                return "";
            var result = new StringBuilder();
            //type=GUID
            var stringGuidList = from f in filters where f.type != null && f.type == "GUID" select f;
            foreach (var i in stringGuidList)
            {
                result.Append(a + i.field + " = GUID'" + i.value + "' and ");
            }
            //type=string
            var stringList = from f in filters where f.type == null && f.op == "contains" select f;
            foreach (var i in stringList)
            {
                result.Append(a + i.field + " like '%" + i.value + "%' and ");
            }
            //type=stringlist
            var stringlistList = from f in filters where f.type != null && f.type == "stringlist" select f;
            foreach (var i in stringlistList)
            {
                var valuelist = i.value.Split(',');
                var inlist = "";
                for (var j = 0; j < valuelist.Count(); j++)
                {
                    if (inlist == "")
                    {
                        inlist = "'" + valuelist[j] + "'";
                    }
                    else
                    {
                        inlist = inlist + "," + "'" + valuelist[j] + "'";
                    }
                }
                result.Append(a + i.field + " in( " + inlist + ") and ");
            }
            //type=boolean
            var booleanList = from f in filters where f.type != null && f.type == "boolean" select f;
            foreach (var i in booleanList)
            {
                result.Append(a + i.field + "=" + (i.value.ToLower() == "true" ? 1 : 0) + " and ");
            }
            //type=numeric
            var numericList = from f in filters where f.type != null && f.type == "numeric" group f by f.field into g select g;
            foreach (var i in numericList)
            {
                result.Append("( ");
                var iiStr = i.Aggregate(string.Empty, (current, ii) => current + (a + ii.field + GetComparison(ii.op) + ii.value + " and "));
                result.Append(iiStr.Substring(0, iiStr.Length - 4));
                result.Append(" )");
                result.Append(" and ");
            }
            //type=date
            var dateList = from f in filters where f.type != null && f.type == "datebox" group f by f.field into g select g;
            foreach (var i in dateList)
            {
                result.Append("( ");
                var iiStr = i.Aggregate(string.Empty, (current, ii) => current + ("" + a + ii.field + "" + GetComparison(ii.op) + "DateTime'" + Convert.ToDateTime(ii.value).ToString("yyyy-MM-dd HH:mm") + "' " + " and "));
                result.Append(iiStr.Substring(0, iiStr.Length - 4));
                result.Append(" )");
                result.Append(" and ");
            }
            //type=list  :"1,2"
            var listList = from f in filters where f.type != null && f.type == "list" select f;
            foreach (var i in listList)
            {
                if (i.value == "NULL")
                    result.Append(a + i.field + " is null and ");
                else
                    result.Append(a + i.field + " in ('" + i.value.Replace("\"", "'").Replace(",", "','") + "') and ");
            }

            return result.ToString().Substring(0, result.Length - 4);
        }

        private static string GetComparison(string comparison)
        {
            var res = string.Empty;
            switch (comparison)
            {
                case "less":
                    res = "<";
                    break;
                case "greater":
                    res = ">";
                    break;
                case "equal":
                    res = "=";
                    break;
                case "notequal":
                    res = "!=";
                    break;
            }
            return res;
        }
        #endregion
    }
}
